package com.demo.util;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Field 辅助类
 */
@SuppressWarnings("unchecked")
public class Fields {
	private static final Map<String, Field> fieldCache = new ConcurrentHashMap<String, Field>(16);
	// 当前对象
	private Object obj;
	// parentClass
	private Class<?> cls;
	// 子 Fields
	private Fields children;
	// 根据 name 所得的 Field
	private Field field;

	public Fields(Object obj, Field field) {
		this.obj = obj;
		this.field = field;
		this.cls = field.getDeclaringClass();
		this.children = null;
	}

	public Fields(Object obj, String name) {
		this(obj, obj.getClass(), name);
	}

	public Fields(Object obj, Class<?> cls, String name) {
		this.obj = obj;
		this.cls = cls;

		if (name.indexOf(".") >= 0) {
			int idx = name.indexOf(".");
			String t_name = name.substring(0, idx);
			Object t_obj = getValue(obj, cls, t_name);

			field = getField(cls, t_name);
			children = new Fields(t_obj, name.substring(idx + 1));
		} else {
			field = getField(cls, name);
		}
	}

	/**
	 * 获取子 Fields
	 */
	public Fields getChildren() {
		return children;
	}

	/**
	 * 获取 值
	 */
	public <T> T getValue() {
		return getValue(obj, cls, field.getName());
	}

	/**
	 * 赋值
	 */
	public void setValue(Object value) {
		boolean flag = field.isAccessible();
		try {
			field.setAccessible(true);
			field.set(obj, value);
		} catch (Exception e) {
			throw new RuntimeException("没有找到 Field['" + cls.getName() + "." + field.getName() + "'] 或参数异常.");
		} finally {
			field.setAccessible(flag);
		}
	}

	public Object getObj() {
		return obj;
	}

	public Field getField() {
		return field;
	}

	/**
	 * 不检测 static field
	 */
	public boolean isFinal() {
		return !Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers());
	}

	/**
	 * 获取值
	 */
	public static <T> T get(Object obj, String name) {
		return get(obj, obj.getClass(), name);
	}

	/**
	 * 获取值
	 */
	public static <T> T get(Object obj, Field field) {
		return new Fields(obj, field).getValue();
	}

	/**
	 * 获取值
	 */
	public static <T> T get(Object obj, Class<?> cls, String name) {
		Fields fields = new Fields(obj, cls, name);
		while (fields.getChildren() != null) {
			fields = fields.getChildren();
		}
		return fields.getValue();
	}

	/**
	 * 获取值,根据字段类型
	 */
	public static <T> T get(Object obj, Class<T> type) {
		Field field = getField(obj.getClass(), type);
		return get(obj, field);
	}

	/**
	 * 设置值
	 */
	public static void set(Object obj, String name, Object value) {
		set(obj, obj.getClass(), name, value);
	}

	/**
	 * 设置值
	 */
	public static void set(Object obj, Class<?> cls, String name, Object value) {
		Fields fields = new Fields(obj, cls, name);
		while (fields.getChildren() != null) {
			fields = fields.getChildren();
		}
		fields.setValue(value);
	}

	/**
	 * 设置值
	 */
	public static void set(Object obj, Field field, Object value) {
		new Fields(obj, field).setValue(value);
	}

	/**
	 * 设置值,根据字段类型
	 */
	public static void set(Object obj, Class<?> type, Object value) {
		Field field = getField(obj.getClass(), type);
		set(obj, field, value);
	}

	private static <T> T getValue(Object obj, Class<?> cls, String name) {
		Field field = getField(cls, name);
		boolean flag = field.isAccessible();
		try {
			field.setAccessible(true);
			return (T) field.get(obj);
		} catch (Exception e) {
			throw new RuntimeException("没有找到 Field['" + cls.getName() + "." + field.getName() + "'] 或参数异常.");
		} finally {
			field.setAccessible(flag);
		}
	}

	private static Field getField(Class<?> cls, String name) {
		String key = cls.getName() + "." + name;
		Field f = fieldCache.get(key);
		if (f == null) {
			synchronized (fieldCache) {
				f = fieldCache.get(key);
				if (f == null) {
					Class<?> targetClass = cls;
					do {
						try {
							f = cls.getField(name);
						} catch (NoSuchFieldException e) {
							try {
								f = cls.getDeclaredField(name);
							} catch (NoSuchFieldException e1) {
								targetClass = cls.getSuperclass();
							}
						}
					} while (f == null && targetClass != null && targetClass != Object.class);

					if (f == null) {//
						throw new RuntimeException("Field['" + key + "'] 不存在.");
					}

					fieldCache.put(key, f);
				}
			}
		}
		return f;
	}

	/**
	 * 根据类型获取 Field , 只获取第一个
	 */
	private static Field getField(Class<?> cls, Class<?> type) {
		String key = cls.getName() + ":" + type.getName();
		Field f = fieldCache.get(key);
		if (f == null) {
			synchronized (fieldCache) {
				f = fieldCache.get(key);
				if (f == null) {
					Class<?> targetClass = cls;
					Field[] fields = null;
					do {
						fields = cls.getFields();
						for (Field field : fields) {
							if (field.getType().isAssignableFrom(type)) {
								f = field;
								break;
							}
						}
						
						fields = cls.getDeclaredFields();
						for (Field field : fields) {
							if (field.getType().isAssignableFrom(type)) {
								f = field;
								break;
							}
						}
						
						targetClass = cls.getSuperclass();
					} while (f == null && targetClass != null && targetClass != Object.class);

					if (f == null) {//
						throw new RuntimeException("class['" + cls.getName() + "'] 中部不存在类型 Field['" + type.getName() + "'].");
					}

					fieldCache.put(key, f);
				}
			}
		}
		return f;
	}
}
